Completed
Push — master ( cd809c...87e5d3 )
by Sander
01:09
created

background.js ➔ getMinedData   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 13
rs 9.4285
1
/* global API */
2
3
var background = (function () {
4
    var storage = new API.Storage();
5
    var _self = this;
6
    var _window = {};
7
8
9
10
    API.runtime.onConnect.addListener(function (port) {
11
12
        port.onMessage.addListener(function (msg) {
13
            if (msg === 'credential_amount') {
14
                port.postMessage('credential_amount:' + local_credentials.length);
15
            }
16
17
        });
18
19
    });
20
21
    var master_password = null;
22
23
    function getMasterPasswordSet() {
24
        return (master_password !== null);
25
    }
26
27
    _self.getMasterPasswordSet = getMasterPasswordSet;
28
29
    function setMasterPassword(opts) {
30
        master_password = opts.password;
31
        if (opts.hasOwnProperty('savePassword') && opts.savePassword === true) {
32
            // Save the password in plain text on user request.
33
            // No secure local storage is available :/
34
            storage.set('master_password', opts.password);
35
        } else {
36
            storage.set('master_password', null);
37
        }
38
39
        if (opts.password) {
40
            getSettings();
41
        } else {
42
            displayLogoutIcons();
43
        }
44
45
    }
46
47
    _self.setMasterPassword = setMasterPassword;
48
49
50
    var testMasterPasswordAgainst;
51
52
    function isMasterPasswordValid(password) {
53
        //return true;
54
        try {
55
            PAPI.decryptString(testMasterPasswordAgainst, password);
56
            return true;
57
        } catch (e) {
58
            return false;
59
        }
60
    }
61
62
    _self.isMasterPasswordValid = isMasterPasswordValid;
63
64
65
    var local_credentials = [];
66
    var local_vault = [];
67
    var encryptedFieldSettings = ['default_vault', 'nextcloud_host', 'nextcloud_username', 'nextcloud_password', 'vault_password'];
68
    _self.settings = {};
69
    _self.ticker = null;
70
    _self.running = false;
71
    function getSettings() {
72
73
        storage.get('settings').then(function (_settings) {
74
75
            if (!_settings || !_settings.hasOwnProperty('nextcloud_host')) {
76
                return;
77
            }
78
79
            if (!master_password && _settings.hasOwnProperty('nextcloud_username') && _settings.hasOwnProperty('vault_password')) {
80
                _self.settings.isInstalled = 1;
81
                testMasterPasswordAgainst = _settings.nextcloud_username;
82
                return;
83
            }
84
85
            for (var i = 0; i < encryptedFieldSettings.length; i++) {
86
                var field = encryptedFieldSettings[i];
87
                _settings[field] = JSON.parse(PAPI.decryptString(_settings[field], master_password));
88
            }
89
90
91
            _self.settings = _settings;
92
93
            if(!_self.settings.hasOwnProperty('ignored_sites')){
94
                _self.settings.ignored_sites = [];
95
            }
96
97
            PAPI.host = _settings.nextcloud_host;
98
            PAPI.username = _settings.nextcloud_username;
99
            PAPI.password = _settings.nextcloud_password;
100
            if (!_settings.vault_password) {
101
                return;
102
            }
103
            if (PAPI.credentialsSet()) {
104
                getCredentials();
105
                if (_self.running) {
106
                    clearInterval(_self.ticker);
107
                }
108
109
                _self.running = true;
110
                _self.ticker = setInterval(function () {
111
                    getCredentials();
112
                }, _self.settings.refreshTime * 1000);
113
            } else {
114
                console.log('Login details are missing!');
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
115
            }
116
        });
117
    }
118
119
    _self.getSettings = getSettings;
120
121
    function getRuntimeSettings() {
122
        return _self.settings;
123
    }
124
125
    _self.getRuntimeSettings = getRuntimeSettings;
126
127
    function getSetting(name) {
128
        return _self.settings[name];
129
    }
130
131
    _self.getSetting = getSetting;
132
133
    function saveSettings(settings, cb) {
0 ignored issues
show
Unused Code introduced by
The parameter cb is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
134
        for (var i = 0; i < encryptedFieldSettings.length; i++) {
135
            var field = encryptedFieldSettings[i];
136
            settings[field] = PAPI.encryptString(JSON.stringify(settings[field]), master_password);
137
        }
138
        PAPI.host = settings.nextcloud_host;
139
        PAPI.username = settings.nextcloud_username;
140
        PAPI.password = settings.nextcloud_password;
141
142
        if(!settings.hasOwnProperty('ignored_sites')){
143
            settings.ignored_sites = [];
144
        }
145
146
        //window.settings contains the run-time settings
147
        _self.settings = settings;
148
149
150
151
        storage.set('settings', settings).then(function () {
152
            getSettings();
153
        });
154
155
    }
156
157
    _self.saveSettings = saveSettings;
158
159
160
    function getCredentials() {
161
        //console.log('Loading vault with the following settings: ', settings);
162
        var tmpList = [];
163
        PAPI.getVault(_self.settings.default_vault.guid, function (vault) {
164
            if (vault.hasOwnProperty('error')) {
165
                return;
166
            }
167
            var _credentials = vault.credentials;
168
            for (var i = 0; i < _credentials.length; i++) {
169
                var key = _self.settings.vault_password;
170
                var credential = _credentials[i];
171
                if (credential.hidden === 1) {
172
                    continue;
173
                }
174
                var usedKey = key;
175
                //Shared credentials are not implemented yet
176
                if (credential.hasOwnProperty('shared_key') && credential.shared_key) {
177
                    usedKey = PAPI.decryptString(credential.shared_key, key);
178
179
                }
180
                credential = PAPI.decryptCredential(credential, usedKey);
181
                if (credential.delete_time === 0) {
182
                    tmpList.push(credential);
183
                }
184
185
            }
186
            delete vault.credentials;
187
            local_vault = vault;
188
            local_credentials = tmpList;
189
            updateTabsIcon();
190
        });
191
    }
192
193
    _self.getCredentials = getCredentials;
194
195
    function getCredentialsByUrl(_url, sender) {
0 ignored issues
show
Unused Code introduced by
The parameter sender is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
196
        if (!master_password) {
197
            return [];
198
        }
199
        if (!_url || _url === '') {
200
            return [];
201
        }
202
        var url = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
203
        var found_list = [];
204
        for (var i = 0; i < local_credentials.length; i++) {
205
            if (local_credentials[i].url && local_credentials[i].username && local_credentials[i].password) {
206
                if (local_credentials[i].url.indexOf(url) !== -1) {
207
                    found_list.push(local_credentials[i]);
208
                }
209
            }
210
        }
211
        return found_list;
212
    }
213
214
    _self.getCredentialsByUrl = getCredentialsByUrl;
215
216
217
    function getCredentialForHTTPAuth(req) {
218
        return getCredentialsByUrl(req.url)[0];
219
    }
220
221
    _window.getCredentialForHTTPAuth = getCredentialForHTTPAuth;
222
223
    var mined_data = [];
224
225
    function minedForm(data, sender) {
226
        var url = sender.url;
227
        var existingLogins = getCredentialsByUrl(sender.url);
228
        var title = API.i18n.getMessage('detected_new_login') + ':';
229
        var minedMatchingID = null;
230
        for (var j = 0; j < existingLogins.length; j++) {
231
            var login = existingLogins[j];
232
            if (login.username === data.username) {
233
                if (login.password !== data.password) {
234
                    minedMatchingID = login.guid;
235
                    title = API.i18n.getMessage('detected_changed_login') + ':';
236
                }
237
                else {
238
                    //console.log('No changes detected');
239
                    delete mined_data[sender.tab.id];
240
                    return;
241
                }
242
            }
243
        }
244
        mined_data[sender.tab.id] = {
245
            title: title,
246
            url: url,
247
            username: data.username,
248
            password: data.password,
249
            label: sender.title,
250
            guid: minedMatchingID
251
        };
252
253
        //console.log('Done mining, ', mined_data, sender.tab.id);
254
    }
255
256
    _self.minedForm = minedForm;
257
258
    function getMinedData(args, sender) {
259
        //console.log('Fecthing  mined data for tab id', sender.tab.id)
260
        var senderUrl = sender.tab.url;
261
        var site = processURL(senderUrl, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
262
        var matches = _self.settings.ignored_sites.filter(function (item) {
263
            return typeof item === 'string' && site.indexOf(item) > -1;
264
        });
265
266
        if(matches.length !== 0){
267
            return null;
268
        }
269
        return mined_data[sender.tab.id];
270
    }
271
272
    _self.getMinedData = getMinedData;
273
274
    function clearMined(args, sender) {
275
        delete mined_data[sender.tab.id];
276
    }
277
278
    _self.clearMined = clearMined;
279
280
    function saveMinedCallback(args) {
281
        createIconForTab(args.sender.tab);
282
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
283
            API.tabs.sendMessage(args.sender.tab.id, {method: "minedLoginSaved", args: args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
284
            });
285
        });
286
    }
287
288
    function ignoreSite(_url) {
289
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
290
            _self.settings.ignored_sites = [];
291
        }
292
        var site = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
293
        if (_self.settings.ignored_sites.indexOf(site) === -1) {
294
            _self.settings.ignored_sites.push(site);
295
            saveSettings(_self.settings);
296
        }
297
    }
298
299
    _self.ignoreSite = ignoreSite;
300
301
    function passToParent(args, sender) {
302
        API.tabs.sendMessage(sender.tab.id, {method: args.injectMethod, args: args.args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
303
        });
304
    }
305
306
    _self.passToParent = passToParent;
307
308
    function getActiveTab(opt) {
309
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
310
            var tab = tabs[0];
311
            API.tabs.sendMessage(tab.id, {method: opt.returnFn, args: tab}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
312
            });
313
        });
314
    }
315
316
    _self.getActiveTab = getActiveTab;
317
318
    function updateCredentialUrlDoorhanger(login) {
319
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
320
            var tab = tabs[0];
321
            var data = login;
322
            data.url = tab.url;
323
            data.title = API.i18n.getMessage('detected_changed_url') + ':';
324
            API.tabs.sendMessage(tab.id, {
325
                method: 'showUrlUpdateDoorhanger',
326
                args: {data: data}
327
            });
328
        });
329
    }
330
331
    _self.updateCredentialUrlDoorhanger = updateCredentialUrlDoorhanger;
332
333
    function updateCredentialUrl(data, sender) {
334
        mined_data[sender.tab.id] = data;
335
        saveMined({}, sender);
336
337
    }
338
339
    _self.updateCredentialUrl = updateCredentialUrl;
340
341
    function saveMined(args, sender) {
342
        var data = mined_data[sender.tab.id];
343
        var credential,
344
            credential_index;
345
346
        if (data.guid === null) {
347
            credential = PAPI.newCredential();
348
        } else {
349
            for (var i = 0; i < local_credentials.length; i++) {
350
                if (local_credentials[i].guid === data.guid) {
351
                    credential = local_credentials[i];
352
                    credential_index = i;
353
                    break;
354
                }
355
            }
356
        }
357
        credential.username = data.username;
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
358
        credential.password = data.password;
359
        credential.url = sender.tab.url;
360
        if (credential.guid !== null) {
361
            PAPI.updateCredential(credential, _self.settings.vault_password, function (updatedCredential) {
362
                if (credential_index) {
363
                    local_credentials[credential_index] = updatedCredential;
364
                }
365
                saveMinedCallback({credential: credential, updated: true, sender: sender});
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
366
                delete mined_data[sender.tab.id];
367
            });
368
        } else {
369
            credential.label = sender.tab.title;
370
            credential.vault_id = local_vault.vault_id;
371
            PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
372
                saveMinedCallback({credential: credential, updated: false, sender: sender});
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
373
                local_credentials.push(createdCredential);
374
                delete mined_data[sender.tab.id];
375
            });
376
        }
377
    }
378
379
    _self.saveMined = saveMined;
380
381
    function searchCredential(searchText) {
382
        var searchFields = ['label', 'username', 'email', 'url', 'description'];
383
        var results = [];
384
        for (var i = 0; i < local_credentials.length; i++) {
385
            var credential = local_credentials[i];
386
            for (var f = 0; f < searchFields.length; f++) {
387
                var field = searchFields[f];
388
                if (credential[field] && credential[field].indexOf(searchText) !== -1) {
389
                    results.push(credential);
390
                    break;
391
                }
392
            }
393
        }
394
        return results;
395
    }
396
397
    _self.searchCredential = searchCredential;
398
399
400
    function injectCreateCredential(args, sender) {
401
        var credential = PAPI.newCredential();
402
        credential.label = args.label;
403
        credential.username = args.username;
404
        credential.password = args.password;
405
        credential.vault_id = local_vault.vault_id;
406
        credential.url = sender.tab.url;
407
        PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
408
            saveMinedCallback({credential: credential, updated: false, sender: sender, selfAdded: true});
409
            local_credentials.push(createdCredential);
410
411
        });
412
    }
413
414
    self.injectCreateCredential = injectCreateCredential;
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
415
416
    function isVaultKeySet() {
417
        return (_self.settings.vault_password !== null);
418
    }
419
420
    _self.isVaultKeySet = isVaultKeySet;
421
422
    function isAutoFillEnabled() {
423
        if (!_self.settings.hasOwnProperty('disableAutoFill')) {
424
            return true;
425
        }
426
        return (_self.settings.disableAutoFill === false);
427
    }
428
429
    _self.isAutoFillEnabled = isAutoFillEnabled;
430
431
    var doorhangerData = null;
432
    function setDoorhangerData(data) {
433
        doorhangerData = data;
434
    }
435
    _self.setDoorhangerData = setDoorhangerData;
436
437
    function getDoorhangerData() {
438
        return doorhangerData;
439
    }
440
    _self.getDoorhangerData = getDoorhangerData;
441
442
    API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
443
444
        if (!msg || !msg.hasOwnProperty('method')) {
445
            return;
446
        }
447
        var result = false;
448
        if (_self[msg.method]) {
449
            result = _self[msg.method](msg.args, sender);
450
        } else {
451
            console.warn('[NOT FOUND] Method call', msg.method, 'args: ', msg.args);
452
        }
453
454
        sendResponse(result);
455
    });
456
457
    var defaultColor = '#0082c9';
458
459
    function createIconForTab(tab) {
460
        if (!master_password) {
461
            return;
462
        }
463
        var tabUrl = tab.url;
464
        var logins = getCredentialsByUrl(tabUrl);
465
        if (tab.active) {
466
            window.contextMenu.setContextItems(logins);
467
        }
468
        var credentialAmount = logins.length;
469
        API.browserAction.setBadgeText({
470
            text: credentialAmount.toString(),
471
            tabId: tab.id
472
        });
473
        API.browserAction.setBadgeBackgroundColor({
474
            color: defaultColor,
475
            tabId: tab.id
476
        });
477
478
        var plural = (credentialAmount === 1) ? API.i18n.getMessage('credential') : API.i18n.getMessage('credentials');
479
        API.browserAction.setTitle({
480
            title: API.i18n.getMessage('browser_action_title_login', [credentialAmount.toString(), plural.toString()]),
481
            tabId: tab.id
482
        });
483
    }
484
485
    function displayLogoutIcons() {
486
        if (_self.settings) {
487
            API.tabs.query({}).then(function (tabs) {
488
                for (var t = 0; t < tabs.length; t++) {
489
                    var tab = tabs[t];
490
                    API.browserAction.setBadgeText({
491
                        text: '🔑',
492
                        tabId: tab.id
493
                    });
494
                    API.browserAction.setBadgeBackgroundColor({
495
                        color: '#ff0000',
496
                        tabId: tab.id
497
                    });
498
                    API.browserAction.setTitle({
499
                        title: API.i18n.getMessage('browser_action_title_locked'),
500
                        tabId: tab.id
501
                    });
502
                }
503
            });
504
        }
505
    }
506
507
    function updateTabsIcon() {
508
        API.tabs.query({}).then(function (tabs) {
509
            for (var t = 0; t < tabs.length; t++) {
510
                var tab = tabs[t];
511
                createIconForTab(tab);
512
            }
513
        });
514
    }
515
516
517
    API.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
518
        if (master_password) {
519
            createIconForTab(tab);
520
        } else {
521
            displayLogoutIcons();
522
        }
523
    });
524
525
    API.tabs.onActivated.addListener(function () {
526
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
527
            if (master_password) {
528
                createIconForTab(tabs[0]);
529
            } else {
530
                displayLogoutIcons();
531
            }
532
        });
533
    });
534
535
    displayLogoutIcons();
536
537
    storage.get('master_password').then(function (password) {
538
        if (password) {
539
            master_password = password;
540
            API.api.browserAction.setBadgeBackgroundColor({
541
                color: defaultColor
542
            });
543
        }
544
        getSettings();
545
    }).error(function (error) {
546
        if (error === "Data not found") {
547
            getSettings();
548
        }
549
    });
550
    return _window;
551
}());
552
553